

#include "mol_hw_sha.h"

#ifdef CONFIG_FH_AXI_DMA
#include "fh_axi_dma.h"
#define HASH_DMA_CHN 3
#elif defined (CONFIG_FH_MC_DMA)
#include "fh_mc_dma.h"
#define HASH_DMA_CHN 15
#endif
#include <asm/arch/hardware.h>





#define HASH_CFG	 0
#define RX_FIFO_CFG0	 0x4
#define RX_FIFO_CFG1	 0x8
#define RX_FIFO_CFG2	 0xc
#define REQ_CFG1		 0x10
#define MES_INFO_CFG0	 0x14
#define MES_INFO_CFG1	 0x18
#define MES_INFO_CFG2	 0x1C
#define HMAC_KEY_INFO_CFG0	 0x20
#define HMAC_KEY_INFO_CFG1	 0x24
#define HMAC_KEY_INFO_CFG2	 0x28
#define DEBUG_BUS	 0x2C
#define HASH_H0	 			0x30
#define HASH(x) 			(HASH_H0 + ((x) << 2))
#define HASH_MSG_LEN_CFG0 			0x70
#define HASH_MSG_LEN_CFG1 			0x74
#define HASH_MSG_LEN_CFG2 			0x78
#define HASH_MSG_LEN_CFG3 			0x7c
#define HKEY_LEN_CFG 			    0x80
#define HKEY_CFG0 			    	0x84
#define HKEY_CFG(x) 			   (HKEY_CFG0 + ((x) << 2))

#ifdef CONFIG_MOL_HW_SHA_FHMOD
#define PWDATA_SWAP 			0xcc
#endif

#define HASH_FIFO			    	0x100

#define HASH_MODE_OFS			    	0x0
#define HASH_MODE_MASK			    	0x7
#define HASH_SHA1			    		0x0
#define HASH_SHA256			    		0x1
#define HASH_SHA384			    		0x2
#define HASH_SHA512			    		0x3

#define HASH_START			    		BIT(3)
#define HMAC_START			    		BIT(4)
#define DBG_BUS_SEL_OFS			    	5
#define DBG_BUS_SEL_MASK			    	0x1F70



#define SWAP32(x)               ((((x) & 0x000000ff) << 24) | \
                                 (((x) & 0x0000ff00) <<  8) | \
                                 (((x) & 0x00ff0000) >>  8) | \
                                 (((x) & 0xff000000) >> 24)   \
                                )



static void  hash_dma_wait_ready(void)
{
	//dma_wait_complete(31);
	printf("hash wait dma go..\n");
	wait_dma_xfer_done(HASH_DMA_CHN);
	printf("hash wait dma done..\n");
}



static void dma_hash_set_para(struct dma_transfer *trans, u32 src, u32 dst, u32 count, u32 data_width)
{

	trans->channel_number = HASH_DMA_CHN;
	trans->fc_mode = DMA_M2P;
	trans->dst_add = dst;
	trans->src_add = src;
#ifdef CONFIG_MOL_HW_SHA_FHMOD
    SET_REG(PWDATA_SWAP+HASH_REG_BASE,0x0123);
    SET_REG(RX_FIFO_CFG0+HASH_REG_BASE,0x1f1ffcfc);
    trans->src_msize = DW_DMA_SLAVE_MSIZE_4;
	trans->dst_msize = DW_DMA_SLAVE_MSIZE_4;
#elif defined (CONFIG_FH_MC_DMA)
	trans->data_switch = SWT_ABCD_DCBA;
	trans->src_msize = DW_DMA_SLAVE_MSIZE_32;
	trans->dst_msize = DW_DMA_SLAVE_MSIZE_32;
#endif
	trans->src_inc_mode = DW_DMA_SLAVE_INC;
	trans->dst_inc_mode = DW_DMA_SLAVE_FIX;
#ifdef CONFIG_FH_MC_DMA
	trans->dst_per = 36;
#elif defined (CONFIG_MOL_HW_SHA_FHMOD)
	trans->dst_per = SPI2_RX;
#endif
	if(data_width == 1){
		trans->src_width = DW_DMA_SLAVE_WIDTH_8BIT;
		trans->dst_width = DW_DMA_SLAVE_WIDTH_8BIT;
	}
	else if (data_width == 2){
		trans->src_width = DW_DMA_SLAVE_WIDTH_16BIT;
		trans->dst_width = DW_DMA_SLAVE_WIDTH_16BIT;
	}
	else{
		trans->src_width = DW_DMA_SLAVE_WIDTH_32BIT;
		trans->dst_width = DW_DMA_SLAVE_WIDTH_32BIT;
	}
	trans->trans_len = count;

    unsigned int dmaonce = (1<<trans->dst_msize)*(1<<(trans->dst_width+1));
    if (dmaonce >= count*4) {
        dmaonce = count*4;
    }
    SET_REG(MES_INFO_CFG2+HASH_REG_BASE,dmaonce);
}




void hash_dma_write_data(u8 *src,u32 len)
{
	void * dma_lli_head;
	struct dma_transfer dma_cp_transfer = {0};

	dma_lli_head = get_dma_rx_lli_head();
	dma_hash_set_para(&dma_cp_transfer, (u32)src, HASH_REG_BASE + HASH_FIFO, len, 4);
	handle_single_transfer(&dma_cp_transfer, dma_lli_head);
}



static void hash_xfer_data(void* buff,unsigned long buflen) 
{
    SET_REG(HASH_REG_BASE+HASH_MSG_LEN_CFG0,buflen);
	if(buflen > 0x100) {
		SET_REG(HASH_REG_BASE+REQ_CFG1,2);
    }

    SET_REG(HASH_REG_BASE+HASH_MSG_LEN_CFG0,buflen);
	buflen = (buflen +3) >> 2 << 2;

	hash_dma_write_data(buff,buflen/4);

	if(buflen > 0x100) {
	    SET_REG(HASH_REG_BASE+REQ_CFG1,0);
	}
}
static int sha1(void* buff,unsigned long buflen,char* res)
{
    printf("sha1\n");
    hash_xfer_data(buff,buflen);
    
    SET_REG(HASH_REG_BASE+HASH_CFG,0x2008);
	
	hash_dma_wait_ready();	
    while(((GET_REG(HASH_REG_BASE+0xc4))&0x10) != 0x10);

    if (res != NULL) {
        memcpy(res,HASH_REG_BASE+HASH_H0,20);
    }

    SET_REG(HASH_REG_BASE+HASH_CFG,0);

    return 0;
}

static int sha256(void* buff,unsigned long buflen,char* res)
{
    printf("sha256\n");
    hash_xfer_data(buff,buflen);
    
    SET_REG(HASH_REG_BASE+HASH_CFG,0x2009);
	
	hash_dma_wait_ready();
    while(((GET_REG(HASH_REG_BASE+0xc4))&0x10) != 0x10);
    
    if (res != NULL) {
        memcpy(res,HASH_REG_BASE+HASH_H0,32);
    }
    SET_REG(HASH_REG_BASE+HASH_CFG,0);

    return 0;
}

static int sha384(void* buff,unsigned long buflen,char* res)
{
    printf("sha384\n");
    hash_xfer_data(buff,buflen);
    
    SET_REG(HASH_REG_BASE+HASH_CFG,0x200a);
	
	hash_dma_wait_ready();
    while(((GET_REG(HASH_REG_BASE+0xc4))&0x10) != 0x10);


	
    if (res != NULL) {
        memcpy(res,HASH_REG_BASE+HASH_H0,48);
       
    }


    SET_REG(HASH_REG_BASE+HASH_CFG,0);
    return 0;
}

static int sha512(void* buff,unsigned long buflen,char* res)
{
    printf("sha512\n");

    hash_xfer_data(buff,buflen);
    
    SET_REG(HASH_REG_BASE+HASH_CFG,0x200b);
	
	hash_dma_wait_ready();	
    while(((GET_REG(HASH_REG_BASE+0xc4))&0x10) != 0x10);


    if (res != NULL) {
        memcpy(res,HASH_REG_BASE+HASH_H0,64);
    }



    SET_REG(HASH_REG_BASE+HASH_CFG,0);
    return 0;
}

static int sha256_hmac(void* buff,unsigned long buflen,void* hkey,unsigned long hkeysize,char* res)
{
    int i = 0;
    int hkeycpylen = hkeysize % 4 == 0? (hkeysize +1) : (hkeysize/4 + 1)*4;

    printf("sha256_hmac\n");


    hash_xfer_data(buff,buflen);
    SET_REG(HASH_REG_BASE+HKEY_LEN_CFG,hkeysize);
    for (i =0 ; i<(hkeycpylen/4+1);i++) {
        unsigned long val = SWAP32(GET_REG(hkey+i*4));
        SET_REG(HASH_REG_BASE+HKEY_CFG0+i*4,val);
    }


    SET_REG(HASH_REG_BASE+HASH_CFG,0x201d);
	
	hash_dma_wait_ready();	
    while(((GET_REG(HASH_REG_BASE+0xc4))&0x10) != 0x10);


    if (res != NULL) {
        memcpy(res,HASH_REG_BASE+HASH_H0,32);
    }


    SET_REG(HASH_REG_BASE+HASH_CFG,0);
    return 0;
}



int hw_sha_mol(HW_HASH_MODE mode,void* buff,void* hkey, unsigned long buflen ,unsigned long hkeysize,char* res)
{
    int ret = -1;
    if (mode == HW_HASH_MODE_SHAHMAC && hkey == NULL) {
        return -1;
    }
    if (buflen == 0) {
        return -1;
    }
#ifdef CONFIG_MOL_HW_SHA_FHMOD
    fh_set_dma_hashdmasel(0);
#endif

    switch (mode) {
    case HW_HASH_MODE_SHA1:
        ret = sha1(buff,buflen,res);
        break;
    case HW_HASH_MODE_SHA256:
        ret = sha256(buff,buflen,res);
        break;
    case HW_HASH_MODE_SHA384:
        ret = sha384(buff,buflen,res);
        break;
    case HW_HASH_MODE_SHA512:
        ret = sha512(buff,buflen,res);
        break;
    case HW_HASH_MODE_SHAHMAC:
        ret = sha256_hmac(buff,buflen,hkey,hkeysize,res);
        break;

    default:
        ret = -1;
        break;
    }
#ifdef CONFIG_MOL_HW_SHA_FHMOD
    fh_set_dma_hashdmasel(1);
#endif
    return ret;
}
    
